
// 启用本插件开关
let IS_ENABLED = true;
// 引擎版本,用于兼容api
let ENGINE_VERSION = cc.ENGINE_VERSION ? cc.ENGINE_VERSION.split(".") : [0,0]
// 模板数量
let Transition = cc.Enum({
    "Layout Mode 0": 0,
    "Layout Mode 1": 1,
    "Layout Mode 2": 2,
    "Layout Mode 3": 3,
    "Layout Mode 4": 4,
    "Layout Mode 5": 5,
    "Layout Mode 6": 6,
    "Layout Mode 7": 7,
    "Layout Mode 8": 8,
    "Layout Mode 9": 9,
    "Layout Mode 10": 10,
});

// 模板需要记录跟踪的组件属性。单纯字符、数字类型属性可直接在InheritedAttr里添加新配置
let InheritedAttr = 
{
	// Node的属性继承 
	"cc.Node":["x", "y", "scaleX", "scaleY", "anchorX", "anchorY", "width", "height", "active","opacity","color", ENGINE_VERSION[0]>=2 && ENGINE_VERSION[1]>=1 ? "angle" : "rotation"], // color 特殊属性必须在 HandleAttr 里添加自定义处理方法
	"cc.Label":["enabled","string","horizontalAlign","verticalAlign","overflow","fontSize"],
	"cc.Widget":["enabled","top","bottom","left","right","horizontalCenter","verticalCenter"],
	"cc.Sprite":["enabled","spriteFrame"], // spriteFrame 特殊属性必须在 HandleAttr 里添加自定义处理方法
	"cc.Layout":["enabled"],

	// 嵌套组件属性继承
	"prefab-ex":["template","template_name"],
};

// 必须记录的属性
let MustSaveAttr = {"template":1,"template_name":1};

// 特殊属性处理函数,对象类型属性需要额外做处理
let HandleAttr = {

	// 节点属性事件处理
	"cc.Node":
	{
		// color 是对象类型要特殊处理
		// 属性事件
		"color":{
		    // compObj == cc.Node
			get(compObj){
				let color = {};
				color.r = compObj.color.r; 
				color.b = compObj.color.b;
				color.g = compObj.color.g;
				return color;
			},
			// 设置值
			set(compObj,color){
				compObj.color = new cc.Color(color.r,color.g,color.b);
			},

			// 新旧值对比有无差异, new_v = cc.Node.color
			compare(old_v,new_v){
				return old_v.r == new_v.r && old_v.b == new_v.b && old_v.g == new_v.g;
			}
		}
	},
	
	// 精灵属性事件处理
	"cc.Sprite":{
		// 属性事件
		"spriteFrame":{
		     // compObj == cc.Sprite
			get(compObj){
				return compObj.spriteFrame && compObj.spriteFrame._uuid;
			},
			// 设置值
			set(compObj,spriteFrameUuid)
			{
				if (!spriteFrameUuid) return compObj.spriteFrame = null;
				
				if (CC_EDITOR)
				{
					if (!funcs.isExistFileByUuid(spriteFrameUuid)){
						cc.warn(compObj.node._ui_path+"切换 spriteFram 失败,不存在图片:",Editor.remote.assetdb.uuidToFspath(spriteFrameUuid))	
						return;
					}

					// 编辑器环境 修改图片
					Editor.Ipc.sendToPanel('scene', 'scene:set-property',{
						id: compObj.uuid,
						path: "spriteFrame",//要修改的属性
						type: "cc.SpriteFrame",
						value: {uuid:spriteFrameUuid},
						isSubProp: false,
					});
				}else{
					// 运行环境 修改图片
					cc.loader.load({"uuid":spriteFrameUuid},(err,spriteFrame) => {
						if (!compObj.isValid) return;
					    compObj.spriteFrame = spriteFrame;
					    if(err) return;
					})
				}
			},

			// 新旧值对比有无差异, spriteFrame = cc.Sprite.spriteFrame
			compare(old_v,spriteFrame){
				return old_v == (spriteFrame && spriteFrame._uuid);
			}
		}
	},

	"cc.Widget":
	{
		// 组件属性赋值完成事件
		onSetEnd(comp,node,attrs){
			// 特殊处理,属性设置完成后调用
			if (!CC_EDITOR && (comp.enabled || comp.isAlignOnce))
			{
				comp.updateAlignment();// 运行环境下设置 cc.Widget 后立即刷新
			}
		},
	},
};

// 日志
let log 	= false ? cc.log : ()=>{};
let logInfo = CC_EDITOR ? Editor.info : cc.log;

// 防止重复弹框
let isAffirmReses = 0;
var funcs = {
	extends: cc.Component,
 
	editor: {   
		executeInEditMode: true,
	},
	
	properties: {  
	    
		prefab:{
			default: null, 
			type: cc.Prefab, 
			displayName: "嵌套预制节点"  
		},
		
		isResesToParent: {
			default: false,
			displayName: "重置预制节点",
			notify: function (oldValue) 	
            {
            	if (this.isResesToParent)
            	{
            		this.isResesToParent = false;
					
            		if (!this.is_only_user_template){
            			delete this.node._init_type;
	            		this.init(true);
            		}else{
            			Editor.info("仅限预制节点嵌套功能使用")
            		}
            	}
            },
		},

		isResesTemplate: {
			default: false,// 
			displayName: "删除当前模板",
			notify: function (oldValue) 	
            {
            	if (this.isResesTemplate ){
            		this.isResesTemplate = false;
            		if(this.cfg && this.cfg[this.template] && confirm("确定重置模板 Layout Mode "+this.template+" ?"))
            		{
            			if (this.isSyncTemplate && (this.template != 0 || isAffirmReses || confirm("是否重置子模板?超过2层预制节点嵌套时不推荐重置。 重置模板:Layout Mode "+this.template+" ?") ) ){
            				// 深度遍历子节点
            				isAffirmReses = 1
            				this.loadChildren(this.node,(child,js)=>{
            					// 同步子节点设置
            					if(js && js.enabled) js.isResesTemplate = true;
            				});
            			}

						let old_v = this.template;
						if (this.template == 0 && isAffirmReses != 3 && (isAffirmReses == 2 || confirm("是否重置运行中节点的实际属性? 单纯模板模式不推荐重置")) )
						{
							// 重置组件属性默认值
							isAffirmReses = 2;
							this.resetChildrenAttr();
						}else
						{
	            			if (old_v == 0)
	            			{
	            				// 零号模板不能为null
	            				this.cfg[old_v] = {}; 
	            				this.cfg.original_attr = {};
	            				// 重新读取默认值
	            				this.loadAllNodeOriginalValue();
	            			}else{
		            			delete this.cfg[old_v];
	            			}
							isAffirmReses = 3;
							this.template = 0;
							this.ui_cfg = JSON.stringify( this.cfg );
						}
						
						setTimeout(()=>{
	            			isAffirmReses = 0;
						},10000)
						logInfo("已重置模板配置: Layout Mode "+old_v);
            		}
            	}
            },
		},

		isSyncTemplate: {
			default: true,// 修改模板时同步修改嵌套子节点的模板表
			displayName: "同步子模板" 
		},

		enabledTemplate: {
			default: false,// 启用后才能生成配置信息
			displayName: "启动模板",
			notify: function (oldValue) 
            {
    			if (this.isSyncTemplate){
    				// 深度遍历子节点
    				this.loadChildren(this.node,(child,js)=>{
    					// 同步子节点设置
    					if(js && js.enabled) js.enabledTemplate = this.enabledTemplate;
    				});
    			}
            },
		},


		ui_cfg: {
			default: '',// 继承后所做的属性修改,请勿修改
			displayName: "配置勿改",
			// notify: function (oldValue) 
   //          {
   //          	cc.log(this.ui_cfg);
   //          }
		},


		 // 自定义模板名
		template_name: {
			default:"",
			displayName: "模板名字", 
		},

		template: {
			displayName: "模板列表", 
            default: Transition["Layout Mode 0"],
            type: Transition,
            animatable: false,
            notify: function (oldValue) 
            {
            	// 非编辑器缓存 || 编辑器内已经初始化 && 不同页面 && 没有在编辑参数
                if( (!CC_EDITOR || this.node._init_type == "ready" && oldValue != this.template) && !this._isInSetAttr) 
                {
                	if (!this.enabledTemplate && this.template != 0){
                		this._isInSetAttr = true;
                		this.template = oldValue;
                		this._isInSetAttr = false;
                		return logInfo("模板功能处于关闭状态,需勾选‘启动模板’");
                	}

                	// 切换时刷新下配置
                	if (CC_EDITOR && this.cfg[oldValue]){
                		this._isInSetAttr = true;
                		let temp = this.template;
                		this.template = oldValue;
                		this.upConfig();
                		this.template = temp;
                		this._isInSetAttr = false;
            		}

                	// 运行环境进来没有这属性
					if(this.cfg == null) {
						this.cfg = this.ui_cfg != "" ? JSON.parse(this.ui_cfg) : {}
					} 

					if(this.isSyncTemplate)
					{
						// 深度遍历子节点
						this.loadChildren(this.node,(child,js)=>{
							// 同步子节点模板设置
							if(js && js.enabled) js.template = this.template;
						});
					}

					if (!this.arrChildPath) {
						// 读取默认值
						this.loadAllNodeOriginalValue();
					}

					// 重置组件属性默认值
					this.resetChildrenAttr();

					// 切换模板
					if(!this.cfg[this.template])
					{
						this.cfg[this.template] = this.cfg[oldValue] ? JSON.parse( JSON.stringify( this.cfg[oldValue] ) ) : {};
						let info = this.cfg[this.template] && this.cfg[this.template][""] && this.cfg[this.template][""]["attr"] && this.cfg[this.template][""]["attr"]["prefab-ex"] || {};
						if (info) {
							delete info.template;
							delete info.enabledTemplate;
						}
						this.ui_cfg = JSON.stringify( this.cfg );
						logInfo("新建模板从 Layout Mode "+oldValue+" 复制属性数据")
					}
					this.inheritCompAttr(this);
                }
            },
        },


	},


	// api: 切换模板
	setTemplate(templateTag)
	{
		let tag = this._isExistTemplate(templateTag);
		if (tag != null){
			this.template = tag;
			return true
		}
		cc.warn("未找到模板:",templateTag);
		return false;
	},
	
	//  api: 是否存在模板
	isExistTemplate(templateTag){
		return this._isExistTemplate(templateTag) != null;
	},

	_isExistTemplate(templateTag){
		if(this.cfg == null) {
			try { 
				this.cfg = this.ui_cfg != "" ? JSON.parse(this.ui_cfg) : {};
			} catch(e) {
				return null;
			}
		} 

		for (var i = 0; i < 100; i++) {
			let temp_info = this.cfg[i];	
			if(!temp_info) break;
			// 寻找名字引索
			if (temp_info[""] && temp_info[""]["attr"] && temp_info[""]["attr"]["prefab-ex"] && temp_info[""]["attr"]["prefab-ex"]['template_name'] == templateTag){
				this.template = i;
				return i;
			}
		}	
		if (!this.cfg[templateTag] || templateTag == "original_attr"){
			return null;
		}

		// 使用的是下标引索
		return templateTag;
	},

	


	start(){},
	
	//1.初始化
	init (isReses) 
	{ 
		let scene = cc.director.getScene();
		
		if(!window.CC_EDITOR || !IS_ENABLED) return; //  运行环境不执行
		if(this.node._init_type != null) return; // 已经初始化

		let is_prefab_file = this.node.parent == scene && scene.name == "New Node";
		if(!this.prefab || is_prefab_file) 
		{
			// 2.自己是预制节点文件,只执行子节点脚本
			log("仅使用模板模式:",this.node.name);
			// 只使用模板记录功能
			this.is_only_user_template = true;
			this.is_prefab_file = is_prefab_file;
			this.loadConfig(this);
			return;
		}
		
		
		this.node._init_type = "ready";
		// 2.使用预制节点嵌套功能
		let item = cc.instantiate(this.prefab);
		let js   = item.getComponent("prefab-ex");
		if (js)
		{
			this.node.parent.insertChild( item ,this.node.getSiblingIndex());
			item._init_type = "new prefab";
			log("克隆自己",this.node.name)
			// 4.使用克隆节点的配置函数
			js.loadConfig(this,isReses);
			// 5.克隆完成移除自己 
			this.node.removeFromParent(true);
			this.node.destroy();
		}else
		{
			// 2.仅使用模板功能,不使用嵌套功能
			this.is_only_user_template = true;
			this.loadConfig(this);
		}
	},
	
	// 3.读取配置 
	loadConfig(oldJs,isReses)
	{
		try
		{			
			this.node._init_type = "ready";
			if (oldJs) this.enabledTemplate = oldJs.enabledTemplate;
			// 执行子节点嵌套脚本
			this.runChildrenComp(this.node,true);
			// 将旧节点的子节点属性继承给克隆节点的子节点们
			if (!this.is_only_user_template && !isReses) this.inheritCompAttrToChildren(oldJs);
			// 读取节点原始值属性值
			this.loadAllNodeOriginalValue();
			// 嵌套模式需要监听节点事件 
			if (!this.is_only_user_template)
			{
				this.bindNodeEvent(this.node);
				this.loadChildren(this.node, (child,js)=>{ if(!js) this.bindNodeEvent(child); });
			}

			// 4.应用父级属性配置
			this.inheritCompAttr(this);
			// 应用子级属性配置覆盖父级属性
			if(this != oldJs && oldJs) 
			{
				// 最上级的节点特殊处理,不启动模板模式情况下继承: 使用之前节点的属性信息: 克隆节点属性 = 旧节点属性;
				this.inheritNodeAttr(oldJs)
				if (!isReses) {
					// 继承上级的模板配置
					this.inheritTemplateCfg(oldJs); 
					this.inheritCompAttr(oldJs);
				}
			}
		} catch(e) 
		{
			// 出错后停止模板功能防止数据配置丢失
			cc.warn("读取模板出错已经停止模板功能,防止数据配置丢失\n",e);
			this.enabled = false;
			return false;
		} 
	},

	// 将旧节点的子节点属性继承给克隆节点的子节点们
	inheritCompAttrToChildren(oldJs){
		this.loadChildren(this.node,(child,now_js)=>{
			if(now_js)
			{
				let path 	 = this.getChildPath(child);
				let old_node = oldJs.getChildByPath(path);
				if (!old_node) return cc.warn("旧节点没有相同路径的节点,可能之前删了预制节点内的子节点,将重新生成配置:"+path);
				let old_js 	 = old_node.getComponent("prefab-ex");
				if(!old_js) return cc.warn("旧节点丢失脚本绑定: prefab-ex ");
				// 继承旧属性
				now_js.inheritTemplateCfg(old_js);
				now_js.inheritCompAttr(old_js);
			}
		},false);
	},

	// 继承模板配置
	inheritTemplateCfg(oldJs){
		oldJs.cfg
		let cfg  		= oldJs.ui_cfg != null && oldJs.ui_cfg != "" ? JSON.parse(oldJs.ui_cfg) : {};
		// 继承模板配置
		this.inheritObj(cfg,this.cfg)
	},

	// 继承对象
	inheritObj(dataA,dataB)
	{
		if (dataA.constructor != dataB.constructor) Editor.warn("inheritObj 类型不对应")
	    for (let key in dataA){
	        let v_a = dataA[key];
	        let v_b = dataB[key];
	        
	        // 判断是否同个类型变量
	        if (v_b == null || v_a.constructor != v_b.constructor )
	        {
	            dataB[key] = v_a;
	        }
	        else if (Array.isArray(v_a))
	        { 
	            v_a.forEach((v,k)=>v_b[k]=v);
	        }else if (v_a instanceof Object)
	        {
	            this.inheritObj(v_a,v_b);
	        }else{
	            dataB[key] = v_a;
	        } 
	    }
	    
	    return dataB;
	},


	inheritNodeAttr(oldJs)
	{
		// 最上级的节点特殊处理,不启动模板模式情况下继承: 使用之前节点的属性信息: 克隆节点属性 = 旧节点属性;
		this.node.setPosition(oldJs.node.getPosition());
		this.node.scaleX = oldJs.node.scaleX;
		this.node.scaleY = oldJs.node.scaleY;
		this.node.opacity = oldJs.node.opacity;
		this.node.active = oldJs.node.active;
		this.node.name = oldJs.node.name;
		this._isInSetAttr 	 = true;
		this.isSyncTemplate  = oldJs.isSyncTemplate;
		this.enabledTemplate = oldJs.enabledTemplate;
		this.template 		 = oldJs.template;
		this._isInSetAttr 	 = false;
		// widget 属性继承
		let comp_old = oldJs.node.getComponent(cc.Widget);
		let comp_new = this.node.getComponent(cc.Widget);
		if (comp_old && comp_new){
			comp_new.top 				= comp_old.top	
			comp_new.bottom 			= comp_old.bottom 
			comp_new.left 				= comp_old.left	
			comp_new.right 				= comp_old.right	
			comp_new.horizontalCenter 	= comp_old.horizontalCenter	
			comp_new.verticalCenter 	= comp_old.verticalCenter	
			// comp_new.updateAlignment()
		}
		log(this.node.name,"的位置:",this.node.x,this.node.y)
	},

	// 继承旧节点属性
	inheritCompAttr(oldJs)
	{
		if (!oldJs) return;

		this._isInSetAttr = true;
		this.template 	  = oldJs.template;
		this._isInSetAttr = false;

		let cfg 	 = this.cfg;
		cfg[oldJs.template] = cfg[this.template] || {};

		let node
		let is_self  = oldJs == this;
		let addChild = is_self ? {} : (cfg.addChild || {} );

		// 转移外部创建的节点到克隆节点内
		for(let uiPath in addChild)
		{
		 	// 被插入新子节点的父级
			let parent = this.arrChildPath[uiPath] || this.node;
			let list = cfg.addChild[uiPath];
			
			for(let key in list || {})
			{
				log("插入节点");
				let childInfo = list[key];
				// 外部加入的节点
				let add_node = oldJs.getChildByPath(parent._ui_path+childInfo.name);
				if (add_node)
				{
					let z = add_node.getSiblingIndex();
					add_node.removeFromParent();
					parent.insertChild( add_node,z || 0);
				}else{ 
					cc.warn("不存在节点1,删除该属性：",uiPath,list);
					delete list[key];
				}
			}
		}

		// 模板功能： 继承旧节点的配置属性
		let comps_info = cfg[oldJs.template];
		for(let uiPath in comps_info || {})
		{
			let info = cfg[oldJs.template][uiPath]; // info={attr:{},addChild:{}}
			let node = this.arrChildPath[uiPath] || this.getChildByPath(uiPath);
			// 禁用模板功能,只能读取本节点属性,过滤其它组件属性
			if (!this.enabledTemplate && node != this.node) continue;
 
			if(node && node.isValid){
				if(node == this.node || !this.checkRename(node))
				{
					this.setChildAttr(node,info.attr);
				}else{
					cc.warn("同级节点路径下存在多个重名节点,不能继承属性");
				}
			}else{
				cc.warn("不存在节点2,删除该属性：",uiPath,info);
				delete cfg[oldJs.template][uiPath];
			}
		}
	},

	// 设置节点属性
	setChildAttr(node,attr)
	{
		this._isInSetAttr = true

		// 继承属性值 
		for(let compName in attr)
		{
			let comp_attr = attr[compName];
			let comp_obj  = compName == "cc.Node" ? node : node.getComponent(compName);
			if (!comp_obj) continue;
			// 禁用模板功能,只能读取本节点属性,过滤其它组件属性
			if (!this.enabledTemplate && comp_obj != this.node) continue;

			for(let key in comp_attr)
			{
				if (HandleAttr[compName] && HandleAttr[compName][key])
				{
					// 特殊属性使用指定方法写入属性
					HandleAttr[compName][key].set(comp_obj,comp_attr[key]);
				}else
				{
					comp_obj[key] = comp_attr[key]; // 写入属性
				}
			}

			// 完成后特殊处理
			if(HandleAttr[compName] && HandleAttr[compName]["onSetEnd"]){
				HandleAttr[compName]["onSetEnd"](comp_obj,node,comp_attr);
			}
		}

		this._isInSetAttr = false
	},

	// 重置子节点属性到修改前
	resetChildrenAttr(){
		let temp = this.template;
		for(let key in this.arrChildPath)
		{
			let node = this.arrChildPath[key];
			let tep1,tep2;

			// template属性不重置
			if (node._old_attr && node._old_attr["prefab-ex"]) {
				tep1 = node._old_attr["prefab-ex"]["template"];
				tep2 = node._old_attr["prefab-ex"]["enabledTemplate"];
				delete node._old_attr["prefab-ex"]["template"];
				delete node._old_attr["prefab-ex"]["enabledTemplate"];
			}
			this.setChildAttr(node,node._old_attr || {});

			if (node._old_attr && node._old_attr["prefab-ex"]){
				node._old_attr["prefab-ex"]["template"] = tep1;
				node._old_attr["prefab-ex"]["enabledTemplate"] = tep2;
			} 
			

		}
	},

	getNodeByUuid(uuid){
		let node;
		this.loadChildren(this.node,(v)=>{
			if (v.uuid == uuid) node = v
		});
		return node;
	},

	// 检测同级目录是否有重名的
	checkRename(node){
		let children = node.parent && node.parent.getChildren();
		let isRename = false;
		children.forEach((v)=>{if(v.name == node.name && v != node) isRename;});
		return isRename;
	},
	

	// 读取预制节点原始属性们
	loadAllNodeOriginalValue()
	{
		this.arrChildPath =  {};
		this.arrOldChildPath = {};
		if (!this.cfg) this.cfg = this.ui_cfg != "" ? JSON.parse(this.ui_cfg) : {};// 仅用模板模式需要 this.cfg

		if (!this.is_prefab_file) this.loadNodeOriginalValue(this.node);
		this.loadChildren(this.node, (child,js)=>{ if(!js) this.loadNodeOriginalValue(child); });

		// 记录预制节点原始的子节点
		for(let uiPath in this.arrChildPath){
			this.arrOldChildPath[uiPath] = this.arrChildPath[uiPath];
		}
	},

	// 读取节点原始值属性值，用于后续对比属性是否有修改, 
	loadNodeOriginalValue(node)
	{
		// 记录原始节点和路径
		let ui_path =  this.getChildPath(node); // ["scene", "view", "btns", "name"]
		if (this.arrChildPath[ui_path] || this.checkRename(node))
		{
			return;
		}

		// 如果节点被改名字或移动位置了则删除之前的记录
		if (node._ui_path && this.arrChildPath[node._ui_path] ) delete this.arrChildPath[node._ui_path]

		// 记录第一次加载的节点
		node._ui_path = ui_path;
		this.arrChildPath[node._ui_path] = node;

		// 运行环境
		if (!window.CC_EDITOR)
		{
			// 如果有原始属性配置
			if (this.cfg.original_attr)
			{
				// 使用配置的原始属性
				node._old_attr = this.cfg.original_attr[node._ui_path]
				if (node._old_attr)
				{
					return;	
				}
				// 否则现场生成
			}
		}


		// 记录节点第一次加载的属性值
		let comps_attr = this.cfg && this.cfg.original_attr ? (this.cfg.original_attr[node._ui_path] || {}) : {};
		let comp_name ;
		let comp ;
		node._old_attr = {}
		

		for(let compName in InheritedAttr)
		{
			// 组件的原始属性记录表
			let comp_attr = node._old_attr[compName] = {}
			comp = compName == "cc.Node" ? node : node.getComponent(compName);
			if(!comp) continue;

			let cfg_attr 	 	= comps_attr[compName];
			let attr_name_list 	= InheritedAttr[compName];
			attr_name_list.forEach((attrName)=>
			{
				if(this.is_only_user_template && cfg_attr && cfg_attr[attrName] != null){
					// 仅使用模板功能，不使用嵌套节点时需要使用之前记录旧属性;
					comp_attr[attrName] = cfg_attr[attrName];
				}
				else if (HandleAttr[compName] && HandleAttr[compName][attrName])
				{
					// 特殊处理 
					comp_attr[attrName] =  HandleAttr[compName][attrName].get(comp); // 特殊处理，记录组件原始属性值	
				}else{
					comp_attr[attrName] = comp[attrName]; // 记录组件原始属性值
				}
			});
		}
		
		/* 生成 node._old_attr 数据结构例子:
			node._old_attr["cc.Node"] = 
			{ 
				x:node.x,
				y:node.y,
				...
			};
		*/ 

	},

	// 获得节点通过路径
	getChildByPath(uiPath){
		let path = uiPath.split("/");
		let node = this.node;
		for (var i = 0; i < path.length; i++) 
		{
			node = path[i] != "" ? node.getChildByName(path[i]) : node;
			if(!node){
				return;
			}
		}	
		return node;
	},


	// 监听子节点属性改变
	bindNodeEvent(node)
	{
		// 仅用于记录新增的节点
		node.on("child-added",(event)=>
		{
			let mvNode = event.detail || event;
			if (!mvNode._ui_path || !this.arrOldChildPath[mvNode._ui_path])
			{
				// 新增节点
				this.setNodeChildCfg(node._ui_path, mvNode);
			}else{
		   		Editor.warn("不支持【移动】预制节点内的原始子节点,该修改不会保存!!");
			}
		});

		node.on("child-removed",(event)=>{
			let uiPath = node._ui_path;
			let mvNode = event.detail || event;
			if (mvNode._ui_path && this.arrOldChildPath[mvNode._ui_path])
			{
				Editor.warn("不支持【删除】预制节点内的原始子节点,该修改不会保存!!");
			}else if(mvNode._ui_path && !this.arrOldChildPath[mvNode._ui_path])
			{
				this.cfg.addChild && this.cfg.addChild[uiPath] ? delete this.cfg.addChild[uiPath][mvNode.name] : ""
				this.cfg[this.template] && this.cfg[this.template][mvNode._ui_path] ? delete this.cfg[this.template][mvNode._ui_path] : ""
			}
		});

		// node.on("active-in-hierarchy-changed",(event)=>{

		// });
	},


	getChildPath(node)
	{
		let path = "";
		let child = node;
		while (child != null && child != this.node) 
		{
			path = child.name + '/'+path;
			child = child.parent;
		}

		// for (let i = path.length - 1; i >= 0; i--) {
		//     node._ui_path.push(path[i]);
		// }
		return path;
	},

	getNodeChangeCfg(uiPath,isCreateCfg=true){
		let cfg = this.cfg[this.template][uiPath];
		if (cfg) return cfg;
		if (isCreateCfg && cfg == null) 
		{
			cfg = this.cfg[this.template][uiPath] = {
				// addChild:{},
				attr:{},
			};
		}
		return cfg;
	},
 

	setNodeChangeCfg(uiPath,compName,attrName,value,compObj)
	{
		let child = this.arrChildPath[uiPath]

		// 特殊属性对比
		if (HandleAttr[compName] && HandleAttr[compName][attrName])
		{
			// 相同属性不记录
			let obj 	= child._old_attr[compName][attrName];
			let info  	= this.getNodeChangeCfg(uiPath,false);
			if(!MustSaveAttr[attrName] && HandleAttr[compName][attrName].compare(obj,value) ){
				if(info && info.attr && info.attr[compName] && info.attr[compName][attrName] != null)  {
					delete info.attr[compName][attrName];
					log("setNodeChangeCfg: 属性值相同删除该属性配置,compName:",compName,"attrName:",attrName,"=",obj,"node:",uiPath);
				}
			}
			else {
				// 有变动情况下才记录配置
				let attr = this.getNodeChangeCfg(uiPath).attr;
				if (!attr && MustSaveAttr[attrName]) return false;

				attr[compName] = attr[compName] || {};
				attr[compName][attrName] = HandleAttr[compName][attrName].get(compObj);
				return true;
			}
		}else
		{
			// 相同属性不记录
			let o_v = child._old_attr[compName][attrName];
			let info  = this.getNodeChangeCfg(uiPath,false);
			if(!MustSaveAttr[attrName] && value == o_v){
				if(info && info.attr && info.attr[compName] && info.attr[compName][attrName] != null) {
					delete info.attr[compName][attrName];
					log("setNodeChangeCfg: 属性值相同删除该属性配置,compName:",compName,"attrName:",attrName,"=",value,"node:",uiPath);
				}
			}
			else {
				// 有变动情况下才记录配置
				let attr = this.getNodeChangeCfg(uiPath).attr;
				if (!attr && MustSaveAttr[attrName]) return false;
				
				attr[compName] = attr[compName] || {};
				attr[compName][attrName] = value;
				return true;
			}
		}
		return false; // 不需要修改
	},

	setNodeChildCfg(uiPath,child){
		if (this.is_only_user_template) return;
		// this.loadNodeOriginalValue(child);
		// this.loadChildren(child, (cv,js)=>{ if(!js) this.bindNodeEvent(cv); });
		// this.bindNodeEvent(child);

		this.cfg.addChild = this.cfg.addChild || {}
		this.cfg.addChild[uiPath] = this.cfg.addChild[uiPath] || {}
		this.cfg.addChild[uiPath][child.name] = {
			 name:child.name,
		};
	},

	// 遍历子节点直到发现嵌套脚本
	loadChildren(node,callback,isOnlyRunComp=true)
	{
		 if (node == null) return cc.warn("没有子节点");
		let nodes = node.getChildren();
		// isOnlyRunComp = true : 从上往下遍历直到发现脚本停止递归遍历
		nodes.forEach((child)=>
		{
			let js   = child.getComponent("prefab-ex")
			if(!js || !isOnlyRunComp)
			{
				this.loadChildren(child,callback,isOnlyRunComp)
			}
			callback(child,js)
		});
	},
	 
	// 执行子节点的复制脚本
	runChildrenComp(node,isMustInit=false)
	{
		if (node == null) return log("没有孩子");
		
		this.loadChildren(node,(child,js)=>{
			if(js && js.enabled)
			{
				if (isMustInit) child._init_type = null; // 必须重新初始化
				js.init(); // 主动调用子节点脚本初始化
			}
		},true)
	},
		
	// 5.生成模板配置
	upConfig(){
		if(this.node._init_type == null || this.arrChildPath == null || !this.enabled) return;

		// 不使用模板功能
		// 刷新检测: 并记录第一次加载的节点属性值
		this.loadChildren(this.node, (child,js)=>{ if(!js) this.loadNodeOriginalValue(child); });
		
		// for 固定的节点
		for (let uiPath in this.arrChildPath) 
		{
			let node = this.arrChildPath[uiPath];
			// 禁用模板功能,只能保存本节点属性
			if (!this.enabledTemplate && this.node != node) continue;

			// for node组件
			for (let compName in node._old_attr) 
			{
				let comp_attr = node._old_attr[compName];
				let comp_obj  = compName == "cc.Node" ? node : node.getComponent(compName)
				if (!comp_obj) continue;
				// 禁用模板功能,只能保存本节点属性
				if (!this.enabledTemplate && comp_obj != node) continue;

				// for 组件属性们
				for (let key in comp_attr) 
				{
					// 组件属性如果有修改则记录则到配置表
					let comp_value = comp_obj[key];
					this.setNodeChangeCfg(uiPath,compName,key,comp_value,comp_obj);
				}
			}
		}

		// 生成字符串保存配置
		// cfg = {"0":{"":{"attr":{"cc.Node":{"x":-325,"y":64}}},"New Node/":{"attr":{"cc.Node":{}}} ,....}
		// 修改前的原始属性数据
		let original_attr = this.removeInvalidConfig();
		this.cfg.original_attr = original_attr;

		this.ui_cfg = JSON.stringify(this.cfg);
	},

	// 删除无效配置数据，压缩
	removeInvalidConfig()
	{
		let user_count = 0;
		// 原始属性
		let original_attr = {} ;
		for(let template in this.cfg)
		{
			if (!isNaN(Number(template)))
			{
				// 以下循环是为了过滤不必要的属性，减少数据大小
				let tem_cfg = this.cfg[template];
				for(let uiPath in tem_cfg)
				{
					if (!this.arrChildPath[uiPath])
					{
						// 删除无效节点路径, 触发原因: 可能是用户删除该子节点
						 delete tem_cfg[uiPath];
						 continue;
					}
					let ogl_comps = original_attr[uiPath] =  ( original_attr[uiPath] || {})
					let comps = tem_cfg[uiPath];
					let is_has_set_n = false
					for(let compName in comps.attr)
					{
						let ogl_comp_attrs 	= ogl_comps[compName] = (ogl_comps[compName] || {})
						let comp_attrs 		= comps.attr[compName];
						let old_comp_attrs  = this.arrChildPath[uiPath]._old_attr[compName];
						let is_has_set_v = false;

						if (old_comp_attrs == null)
						{
							// 删除无效组件配置,触发原因: 可能是用户删除该节点们的组件了
							 delete comps.attr[compName];
							 continue;
						}
						for(let attrName in comp_attrs)
						{
							// 记录原始属性
							ogl_comp_attrs[attrName] = old_comp_attrs[attrName];
							is_has_set_v = true;
							is_has_set_n = true;
						}

						// 删除空表
						if(!is_has_set_v){
							delete comps.attr[compName];
						}
					}

					// 删除空表
					if(!is_has_set_n){
						delete tem_cfg[uiPath];
					}
					delete ogl_comps["prefab-ex"];
					// original_attr[uiPath] = this.arrChildPath[uiPath] ? this.arrChildPath[uiPath]._old_attr : {}; 
				}
			 	user_count ++;
			};
		}

		// 当有两个以上模板设置时，原始属性在运行环境才有使用价值
		return user_count>1 ? original_attr : undefined;
	},

	// 是否存在文件
	isExistFileByUuid(uuid){
		let absP = Editor.remote.assetdb.uuidToFspath(uuid)
		if(!absP) return false;

		let p ;
		let s_ind = absP.lastIndexOf('.');
		if (s_ind != -1){
		    let e_ind = absP.indexOf('/',s_ind)
		    if (e_ind != -1){
		        p = absP.substr(0,e_ind)
		    }else{
		    	p = absP;
		    }
		}

		return p ? Editor.remote.assetdb.existsByPath(p) : false
	},
	 
	onDestroy(){  
	    delete this.node._init_type; // 脚本被删除时调用，防止不能重复初始化
	},
} 
   
cc.Class(funcs);
   
// 本插件的一些事件处理
if (!cc.director._isBindEventPrefabEx && window.CC_EDITOR)
{
	cc.director._isBindEventPrefabEx = true;


	// 记录通过预制节点创建的uuid
	let moduleName = 'electron'
	// 自动将脚本绑定到刚拖进来的节点上
	// let create_prefab_uuid ;
	// require(moduleName).ipcRenderer.on("scene:create-nodes-by-uuids",(list,parentUuid)=>{
	// 	if (list.length == 1){
	// 		create_prefab_uuid = parentUuid;
	// 	}
	// 	log("创建节点by预制");
	// });

	// // 刚创建的节点会通过该事件发出
	// require(moduleName).ipcRenderer.on("scene:query-node",(node_uuid)=>{
	// 	if (node_uuid && create_prefab_uuid)
	// 	{
	// 		// 获得刚创建的节点
	// 		let node = cc.engine.getInstanceById(node_uuid);
	// 		log("尝试绑定脚本",node.name);
	// 	}
	// 	create_prefab_uuid = null
	// });




	// 监听进入预制节点编辑
	let isInPrefabMode = 0;
	require(moduleName).ipcRenderer.on("scene:enter-prefab-edit-mode",()=>{
		log("进入预制节点编辑模式");
		isInPrefabMode = 1;
	}); 
	


	// 0.场景运行后主动调用脚本
	let isUpDateComp = true;
	cc.director.on(cc.Director.EVENT_AFTER_SCENE_LAUNCH,function(event)
	{
		if(!event.detail) return;
		
		if (isInPrefabMode == 0 || event.detail.name == "New Node"){
			
			isInPrefabMode = event.detail.name == "New Node" ? 2 : isInPrefabMode;
			// setTimeout(()=>
			// {
				// funcs.runChildrenComp(cc.director.getScene())
			// },100)
		// 切换到预制节点编辑时场景会刷新两遍 : ipcRenderer.on -> director.on -> director.on 
		}else if (isInPrefabMode == 1 && event.detail.name != "New Node")
		{
			isInPrefabMode = 2;
			
		}else if (isInPrefabMode != 0 && event.detail.name != "New Node")
		{
			log("刚修改完预制节点回来");

			let scene_url =  Editor.remote.assetdb.uuidToUrl(event.detail.uuid)

			if (!scene_url) return;
			let uuid = event.detail.uuid;
			isUpDateComp = false;
			setTimeout(()=>
			{
				isUpDateComp = true;
				isInPrefabMode = 0;
				// 再刷新一遍场景,否则脚本挂载的预制节点文件不会更新，切换场景太快编辑器会报错
				cc.director._loadSceneByUuid(uuid,()=>{});
			},500)
		}
	})

	// 主动刷新脚本的 upConfig 函数，防止update函数在未激活状态读取不来配置
	setInterval(()=>{
		
		let scene = cc.director.getScene();
		if (!scene || !scene.getChildren || !isUpDateComp) return;

		// 调用未初始化的脚本
		funcs.runChildrenComp(cc.director.getScene())

		// 主动调用刷新配置函数
		funcs.loadChildren(scene,(child,js)=>{
			if(js && js.enabled && js.upConfig)
			{
				js.upConfig(); 
			}
		},false)

	},500);
}